/**
 * \file test_suite_sdv_eal_sm4.c
 *
 * \brief SM4 algorithm test cases for SDV EAL
 */
/*
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may
 *  not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "mbedtls/build_info.h"

#if defined(MBEDTLS_SM4_C) && defined(MBEDTLS_CIPHER_MODE_CBC)

#include "mbedtls/sm4.h"
#include "mbedtls/cipher.h"
#include "mbedtls/platform_util.h"

#include <string.h>

#include "test_suite_sdv_eal_sm4.h"

/**
 * \brief SM4-CBC encryption/decryption test case
 *
 * This test verifies that SM4-CBC encryption followed by decryption
 * returns the original plaintext.
 *
 * \param key_hex         SM4 key in hexadecimal string format (32 chars)
 * \param iv_hex          Initialization vector in hexadecimal (32 chars)
 * \param input_hex       Input data in hexadecimal string format
 * \param expected_hex    Expected output (should match input for round-trip)
 */
void test_sm4_cbc_encrypt_decrypt( char *key_hex, char *iv_hex, 
                                 char *input_hex, char *expected_hex )
{
    int ret = 0;
    mbedtls_cipher_context_t ctx;
    unsigned char key[16];
    unsigned char iv[16];
    unsigned char *input = NULL;
    unsigned char *output_enc = NULL;
    unsigned char *output_dec = NULL;
    size_t input_len = 0;
    size_t output_len_enc, output_len_dec;
    size_t iv_len = 16;
    
    /* Initialize context */
    mbedtls_cipher_init( &ctx );
    
    /* Convert hex strings to binary data */
    TEST_HEX2BIN( key, key_hex, 32 );
    TEST_HEX2BIN( iv, iv_hex, 32 );
    
    if( input_hex != NULL && strlen( input_hex ) > 0 )
    {
        input_len = strlen( input_hex ) / 2;
        input = mbedtls_calloc( 1, input_len );
        TEST_HEX2BIN( input, input_hex, strlen( input_hex ) );
    }
    else
    {
        /* Handle empty input case */
        input_len = 0;
        input = mbedtls_calloc( 1, 1 ); /* Allocate at least 1 byte */
    }
    
    /* Allocate output buffers - account for padding */
    output_enc = mbedtls_calloc( 1, input_len + 16 );
    output_dec = mbedtls_calloc( 1, input_len + 16 );
    
    TEST_ASSERT( key_hex != NULL );
    TEST_ASSERT( iv_hex != NULL );
    TEST_ASSERT( input != NULL );
    TEST_ASSERT( output_enc != NULL );
    TEST_ASSERT( output_dec != NULL );
    
    /* Setup SM4-CBC cipher */
    ret = mbedtls_cipher_setup( &ctx, mbedtls_cipher_info_from_type( MBEDTLS_CIPHER_SM4_CBC ) );
    TEST_ASSERT( ret == 0 );
    
    ret = mbedtls_cipher_setkey( &ctx, key, 128, MBEDTLS_ENCRYPT );
    TEST_ASSERT( ret == 0 );
    
    ret = mbedtls_cipher_set_padding_mode( &ctx, MBEDTLS_PADDING_PKCS7 );
    TEST_ASSERT( ret == 0 );
    
    /* Encrypt */
    ret = mbedtls_cipher_crypt( &ctx, iv, iv_len, input, input_len, 
                               output_enc, &output_len_enc );
    TEST_ASSERT( ret == 0 );
    
    /* Reset IV for decryption */
    TEST_HEX2BIN( iv, iv_hex, 32 );
    
    /* Setup for decryption */
    ret = mbedtls_cipher_setkey( &ctx, key, 128, MBEDTLS_DECRYPT );
    TEST_ASSERT( ret == 0 );
    
    /* Decrypt */
    ret = mbedtls_cipher_crypt( &ctx, iv, iv_len, output_enc, output_len_enc, 
                               output_dec, &output_len_dec );
    TEST_ASSERT( ret == 0 );
    
    /* Verify decrypted data matches original input */
    TEST_ASSERT( output_len_dec == input_len );
    TEST_ASSERT( memcmp( input, output_dec, input_len ) == 0 );
    
    /* If expected output provided, verify encrypted output (optional) */
    if( expected_hex != NULL && strlen( expected_hex ) > 0 )
    {
        unsigned char *expected_bin = NULL;
        size_t expected_len = strlen( expected_hex ) / 2;
        
        expected_bin = mbedtls_calloc( 1, expected_len );
        TEST_HEX2BIN( expected_bin, expected_hex, strlen( expected_hex ) );
        
        /* For CBC mode, we can't easily predict ciphertext without 
         * implementing the entire algorithm, so this is mainly for 
         * round-trip verification */
        TEST_ASSERT( memcmp( expected_bin, output_dec, expected_len ) == 0 );
        
        mbedtls_free( expected_bin );
    }
    
exit:
    mbedtls_cipher_free( &ctx );
    mbedtls_free( input );
    mbedtls_free( output_enc );
    mbedtls_free( output_dec );
}

/**
 * \brief SM4-CBC encryption/decryption with various data lengths
 *
 * Tests edge cases for different input lengths to ensure proper padding handling.
 */
void test_sm4_cbc_various_lengths( void )
{
    const char *key_hex = "0123456789ABCDEFFEDCBA9876543210";
    const char *iv_hex = "0123456789ABCDEF0123456789ABCDEF";
    
    /* Test different data lengths */
    const char *test_data[] = {
        "",                            /* Empty data */
        "41",                          /* 1 byte */
        "4142",                        /* 2 bytes */
        "414243",                      /* 3 bytes */
        "41424344",                    /* 4 bytes */
        "4142434445",                  /* 5 bytes */
        "414243444546",                /* 6 bytes */
        "41424344454647",              /* 7 bytes */
        "4142434445464748",            /* 8 bytes (block size) */
        "414243444546474849",          /* 9 bytes */
        "4142434445464748494A",        /* 10 bytes */
        "4142434445464748494A4B4C4D4E4F50"  /* 16 bytes */
    };
    
    for( size_t i = 0; i < sizeof( test_data ) / sizeof( test_data[0] ); i++ )
    {
        test_sm4_cbc_encrypt_decrypt( (char *)key_hex, (char *)iv_hex, 
                                     (char *)test_data[i], (char *)test_data[i] );
    }
}

#endif /* MBEDTLS_SM4_C && MBEDTLS_CIPHER_MODE_CBC */